﻿using Sidvall.Serialization;
using System.Linq;

namespace Sidvall.Data
{
    public class SqlQuery
    {
        #region Public Members

        #region NextParameterIndex

        public int NextParameterIndex
        {
            get
            {
                return this.ParameterStartIndex + this.ParameterValues.Count;
            }
        }

        #endregion
        #region IsValid

        public bool IsValid
        {
            get
            {
                if (_Sql == null)
                    return false;
                if (_Sql.Length == 0)
                    return false;
                return true;
            }
        }

        #endregion
        #region ParameterValues

        private System.Collections.ObjectModel.Collection<object> _ParameterValues;
        public System.Collections.ObjectModel.Collection<object> ParameterValues
        {
            get
            {
                if (_ParameterValues == null)
                    _ParameterValues = new System.Collections.ObjectModel.Collection<object>();
                return _ParameterValues;
            }
        }

        #endregion
        #region Sql

        private System.Text.StringBuilder _Sql;
        public System.Text.StringBuilder Sql
        {
            get
            {
                if (_Sql == null)
                    _Sql = new System.Text.StringBuilder();
                return _Sql;
            }
        }

        #endregion

        #region AddParameterValues

        public void AddParameterValues(params object[] parameterValues)
        {
            if (parameterValues == null)
                return;
            foreach (var parameterValue in parameterValues)
                this.ParameterValues.Add(parameterValue);
        }

        #endregion
        #region Append

        public void Append(string sql)
        {
            Append(sql, null);
        }
        public void Append(string sql, string prefix)
        {
            if (!string.IsNullOrWhiteSpace(sql))
            {
                if (prefix != null)
                    this.Sql.Append(prefix);
                this.Sql.Append(sql);
            }
        }
        public void Append(SqlQuery query)
        {
            Append(query, null);
        }
        public bool Append(SqlQuery query, string prefix)
        {
            if (query == null)
                return false;
            if (!query.IsValid)
                return false;
            foreach (var parameterValue in query.ParameterValues)
                this.ParameterValues.Add(parameterValue);
            if (!string.IsNullOrWhiteSpace(prefix))
                this.Sql.Append(prefix);
            this.Sql.Append("(" + query.Sql.ToString() + ")");

            return true;
        }
        public void Append(System.Collections.Generic.IEnumerable<SqlQuery> queries, LogicalOperator logicalOperator)
        {
            int count;
            bool firstItem;

            if (queries == null)
                return;
            count = queries.Count();
            firstItem = true;
            foreach (var query in queries)
            {
                if (!firstItem)
                {
                    if (logicalOperator == LogicalOperator.And)
                        Append(query, " AND ");
                    else
                        Append(query, " OR ");
                }
                else if (count > 1)
                    firstItem = !Append(query, "(");
                else
                    firstItem = !Append(query, null);
            }
            if ((!firstItem) && (count > 1))
                this.Sql.Append(")");
        }

        #endregion
        #region AppendParameter

        public void AppendParameter(object parameterValue)
        {
            var name = this.ParameterName + this.NextParameterIndex.ToString(System.Globalization.CultureInfo.InvariantCulture);
            this.Sql.Append(name);
            this.ParameterValues.Add(parameterValue);
        }

        #endregion
        #region ToString

        public override string ToString()
        {
            int index = this.ParameterStartIndex;
            string value = this.Sql.ToString();
            if (!string.IsNullOrWhiteSpace(this.ParameterName))
            {
                var pos1 = value.IndexOf(this.ParameterName, System.StringComparison.OrdinalIgnoreCase);
                while ((pos1 >= 0) && (index < this.NextParameterIndex))
                {
                    var pos2 = pos1 + 1;
                    while ((pos2 < value.Length) && (char.IsLetterOrDigit(value[pos2])))
                    {
                        pos2 += 1;
                    }
                    value = value.Substring(0, pos1) + GetParameterValue(this.ParameterValues[index]) + value.Substring(pos2);
                    index += 1;
                    pos1 = value.IndexOf(this.ParameterName, System.StringComparison.OrdinalIgnoreCase);
                }
            }
            return value;
        }

        #endregion

        #endregion
        #region Private Members

        private string ParameterName { get; set; }
        private int ParameterStartIndex { get; set; }

        #region GetParameterValue

        private static string GetParameterValue(object value)
        {
            if (value == null)
                return "NULL";
            var type = DataTypeManager.GetDataTypeFromValue(value);
            switch (type)
            {
                case DataType.Object:
                    return value.ToString(string.Empty);
                default:
                    return Sidvall.Serialization.SerializerManager.Current.SerializeObject(value, type);
            }
        }

        #endregion

        #endregion
        #region Constructors

        public SqlQuery()
        {
        }
        public SqlQuery(string parameterName)
        {
            this.ParameterName = parameterName;
        }
        public SqlQuery(string parameterName, int parameterStartIndex)
        {
            this.ParameterName = parameterName;
            this.ParameterStartIndex = parameterStartIndex;
        }

        #endregion
    }
}
